package communication;

public class ModbusModem {

	private SComm sc;
	private String porta;
	private int baudrate;
	private int timeout;
	private SComm modem;
	byte[] readDataFrame = new byte[8];

	private char[] addresses; // endereços que devem ser requisitados
	private char[] cAddresses;
	private char[] numberToRead;
	private int numberOfMessages = 1; // numero de msgs necessarias para
	// requisicao desses endereços
	private byte[][] msgs; // mensagens que serão enviadas para leitura dos
	private char[] values;
	private int counter;

	// endereços

	ModbusModem() {
		porta = "COM1";
		baudrate = 9600;
		timeout = 1000;

	}

	public boolean Connect(char[] add, char[] cAdd) {
		modem = new SComm(porta, baudrate, timeout);
		if (modem.AbrirPorta()) {
			System.out.println("Conexão MODBUS OK;");
			// modem.HabilitaLeitura();
		} // System.out.println("Porta OK!");
		else {
			System.out.println("Problema na porta!");
			return false;
		}

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		addresses = add;
		cAddresses = cAdd;
		values = new char[addresses.length];

		System.out.println("Número de variáveis lidas por ciclo: "
				+ values.length);

		checkNumberOfMessages();
		constructReadFrames();

		return true;
	}

	public void Close() {
		modem.FecharCom();
	}

	public char[] requestData() {
		counter = 0;
		for (int i = 0; i < numberOfMessages; i++) {
			int count = 0;
			byte[] frameReceived = null;

			do {
				modem.SendFrame(msgs[i]);

				try {
					Thread.sleep(180);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				frameReceived = null;
				frameReceived = modem.getReply();

				if (frameReceived != null) {
					// for (int k = 0; k < frameReceived.length; k++) {
					// System.out.print(+frameReceived[k] + " ");
					// }

					if (checkFrame(msgs[i], frameReceived)) {
						getDados(frameReceived);
						break;
					} else {
						// System.out.println("Erro! Re-envio! ");
						count++;
					}
				} else {
					count++;
					System.out.println("Erro! Re-envio!");
				}

			} while (count < 3);
			if (count == 3) {
				values[counter] = 0xFF;
				counter++;
				System.out.println("Falha no recebimento! ");
			}

		}

		return values;
	}

	public boolean checkFrame(byte[] tx, byte[] rx) {
		if ((tx[0] == rx[0]) && (tx[1] == rx[1])) {
			return true;
		} else
			return false;
	}

	public void getDados(byte[] c) {
		if (c != null) {
			for (int i = 0; i < (c.length - 5) / 2; i += 1) {

				values[counter] = (char) (c[3 + 2 * i] * 0x100 + c[4 + 2 * i]);
				// System.out.println("VALOR " + (3 + 2 * i));
				// System.out.println((counter + 1) + " " + values[counter]);
				counter++;
			}
		} else
			modem.FecharCom();
	}

	private int checkNumberOfMessages() {
		char k = 0x01;
		numberToRead = new char[10];
		for (int i = 0; i < (addresses.length - 1); i++) {
			if (addresses[i] + 1 != addresses[i + 1]) {
				numberToRead[numberOfMessages - 1] = k;
				numberOfMessages++;
				k = 1;
			} else
				k++;

		}
		numberToRead[numberOfMessages - 1] = k;

		// System.out.println("Número de mensagens: " + numberOfMessages);

		return numberOfMessages;
	}

	private void constructReadFrames() {
		msgs = new byte[numberOfMessages][8];
		char j = 0;
		System.out.println("Mensagens que serão enviadas:");
		for (int i = 0; i < numberOfMessages; i++) {
			msgs[i][0] = (byte) cAddresses[i];
			msgs[i][1] = 0x03;

			msgs[i][4] = (byte) (numberToRead[i] >> 8);
			msgs[i][5] = (byte) (numberToRead[i]);
			msgs[i][2] = (byte) (addresses[j] >> 8);
			msgs[i][3] = (byte) (addresses[j]);
			j += numberToRead[i];

			char crc = (char) update(msgs[i], 6, 0);

			// doCRC(msg[i], 6);

			msgs[i][6] = (byte) crc;
			msgs[i][7] = (byte) (crc >> 8);

			System.out.print((i + 1) + " [");
			for (int k = 0; k < 8; k++) {
				System.out.print(+msgs[i][k] + " ");
			}
			System.out.println("]");

		}
		System.out.println("");

	}

	public short update(byte[] data, int end, int start) {
		short aux;
		int shiftBit;
		short generator = (short) 0xA001;

		short register = (short) 0xFFFF; // carrega 0xFFFF para o registrador
		// register = (short)(register ^ 0xFFFF);
		for (int i = start; i <= end - 1; i++) {
			aux = (short) data[i]; // converte o byte a ser calculado para int
			aux = (short) (aux & 0xFF);
			register = (short) (register ^ aux);
			for (int j = 0; j < 8; j++) {
				shiftBit = register % 2;
				register = (short) (register >> 1);
				register = (short) (register & 0x7FFF); // esta linha garante
				// que o bit adicionado
				// à esquerda do numero
				// é 0 mesmo quando seu
				// valor é negativo, o
				// numero é tratado como
				// inteiro(4 bytes) no
				// shift
				if (shiftBit == 1 || shiftBit == -1) { // bit menos
					// significativo é 1
					register = (short) (register ^ generator);
				}
			}
		}
		return register;
	}

}